# api_5101.py
# Create by GF 2025-05-10 23:51

# Python 3 Standard Libraries.
import io
import os
# ..................................................
import flask
# ..................................................
import proxy_openai_api_202505

# ##################################################

WWW_HTML_RESOURCE_URL          = "http://110.41.178.142/resource"
SIMILAR_TO_OPENAI_API_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
                               # "https://spark-api-open.xf-yun.com/v1"
                               # "https://dashscope.aliyuncs.com/compatible-mode/v1"

# ##################################################

LLM_MESSAGE_LIST_HTML_CSS_PPT = [

    # 关于 PPT (PowerPoint) 模板
    {"role": "user",      "content": "当用户问及 PPT 模板信息时, 你需要为用户提供 PPT 模板信息及预览图"},
    {"role": "assistant", "content": "好的, 我将响应用户 PPT 模板信息及模板预览图 (如果有图片链接的话)"},
    {"role": "user",      "content": str('\n').join([
        """这些是已有并且可用的 PPT 模板:""",
        """[{"name": "潘通蓝时尚简洁", "color": "蓝,白", "style": "商务,简约", "url": http://110.41.178.142/resource/office.ppt.template.ptlssjj.0.example.600.338.4.jpg"}]"""])},
    {"role": "assistant", "content": "好的, 我们共有 1 个 PPT 模板"},
    {"role": "user",      "content": "你将以 **![describe](link)** 这种形式将 PPT 模板中的 URL 图像展现给用户"},
    {"role": "assistant", "content": "好的, 我将以 Markdown 图片链接的形式将 PPT 模板中的 URL 图像展现给用户"},
    {"role": "user",      "content": "请给出 1 个蓝白色系的商务 PPT 模板"},
    {"role": "assistant", "content": str('\n').join([
        """![潘通蓝时尚简洁模板](http://110.41.178.142/resource/office.ppt.template.ptlssjj.0.example.600.338.4.jpg)""",
        """模板名称: 潘通蓝时尚简洁""",
        """视觉系统: 蓝 + 白 / 时尚 / 简洁""",
        """专业功能: 数据联动更新""",
        """智能组件: SWOT 分析自动生成器""",
        """适用场景: 工作计划"""])},

    # 关于 PPT (PowerPoint) 大纲
    {"role": "user",      "content": "当用户问及 **PPT (PowerPoint) 大纲** 时, 你需要为用户输出 PPT 大纲并放在 ```outline\n``` 中"},
    {"role": "assistant", "content": "好的, 我将会把 PPT 大纲输出到 ```outline\n``` 中"},
    {"role": "user",      "content": "PPT 大纲中需要明确字体大小 (Font Size)、文字的对齐方式 (Align)、是否应有图表 (Chart)"},
    {"role": "assistant", "content": "好的, 我将在 PPT 大纲信息中明确字体大小 (Font Size)、文字的对齐方式 (Align)、是否应有图表 (Chart)"},
    {"role": "user",      "content": str('\n').join([
        """请帮我生成 PPT 大纲, 共有 2 页, 其中一页是封面, 另一页是内容""",
        """封面中包含: 2025 年工作计划、汇报人苟风""",
        """内容中包含: 设计与开发工作是 2025 年的主要工作, 将持续进行 UI 创新"""])},
    {"role": "user",      "content": str('\n').join([
        """```outline""",
        """1. PPT 封面""",
        """   - 主标题: 2025 年工作计划 (字体大小 48 pt, 居中对齐)""",
        """   - 次级标题: 汇报人 苟风 (字体大小 24 pt, 居中对齐)""",
        """2. PPT 内容 (设计与开发)""",
        """   - 主标题: 设计与开发 (字体大小 24 pt, 左对齐)""",
        """   - 文本内容: 设计与开发工作是 2025 年的主要工作, 将持续进行 UI 创新 (字体大小 16 pt, 左对齐)""",
        """   - 图表内容: 设计工作占比 0.6, 开发工作占比 0.4 (饼图)""",
        """```"""])},
    {"role": "user",      "content": "可以提醒用户复制你的 PPT 大纲, 根据用户自己的需求修改后重新发送给你"},
    {"role": "assistant", "content": "好的, 我将结合用户的新需求和前面已经为用户生成的 PPT 大纲, 重新组织 PPT 大纲"},

    # 关于 PPT (PowerPoint) 生成
    {"role": "user",      "content": "当用户请求生成 PPT 时, 你需要以 HTML + CSS 的方式呈现 PPT 画面, 生成 HTML + CSS 代码, 并放在 ```html\n``` 中"},
    {"role": "assistant", "content": "好的"},
    {"role": "user",      "content": str('\n').join([
        """这些是已有并且可用的 PPT 素材:""",
        """[""",
        """  {"name": "潘通蓝时尚简洁", "page": "封面", "path": "./resource/office.ppt.template.ptlssjj.1.cover.1280.720.jpg"},""",
        """  {"name": "潘通蓝时尚简洁", "page": "目录", "path": "./resource/office.ppt.template.ptlssjj.2.catalog.1280.720.jpg"},""",
        """  {"name": "潘通蓝时尚简洁", "page": "章节", "path": "./resource/office.ppt.template.ptlssjj.2.chapter.1280.720.jpg"},""",
        """  {"name": "潘通蓝时尚简洁", "page": "内容", "path": "./resource/office.ppt.template.ptlssjj.4.content.1280.720.jpg"},""",
        """  {"name": "PPT 模板 (中国石油 2024 版)", "page": "内容", "url": "http://110.41.178.142/resource/ppt.template.cnpc.2024.2.content.1280.720.png"}""",
        """]""",])},
    {"role": "assistant", "content": "好的"},
    {"role": "user",      "content": str('\n').join([
        """这些是已有并且可用的 PPT 素材各页面的文本定位:""",
        """[""",
        """  {"name": "潘通蓝时尚简洁", "page": "封面", "title_zone": [[0.20,0.33],[0.80,0.48]], "sub_title_zone": [[0.33,0.62],[0.66,0.66]], "text_zone": [[0.20,0.50],[0.80,0.57]]},""",
        """  {"name": "潘通蓝时尚简洁", "page": "目录", "title_zone": [],                        "sub_title_zone": [],                        "text_zone": [[0.51,0.15],[0.90,0.85]]},""",
        """  {"name": "潘通蓝时尚简洁", "page": "章节", "title_zone": [[0.15,0.48],[0.81,0.57]], "sub_title_zone": [[0.15,0.31],[0.81,0.45]], "text_zone": [[0.14,0.58],[0.86,0.73]]},""",
        """  {"name": "潘通蓝时尚简洁", "page": "内容", "title_zone": [[0.06,0.02],[0.81,0.57]], "sub_title_zone": [],                        "text_zone": [[0.04,0.16],[0.49,0.90]]},""",
        """  {"name": "PPT 模板 (中国石油 2024 版)", "page": "内容", "size": [1280, 720], "px_zone": {"title": [[20, 20],[1195, 75]], "sub_title": [], "content": [[20, 95], [1260, 700]]}""",
        """]""",
        """其中, [[0.20,0.33],[0.80,0.48]] 表示在图像中从左上角 [w, h] 到右下角 [w, h] 的区域定位的相对数""",
        """其中 px_zone 中的 [[20,20],[1195, 75]] 表示在图像中从左上角 [w, h] 到右下角 [w, h] 的区域定位的像素位置""",])},
    {"role": "assistant", "content": "好的, 我将在生成 HTML + CSS 代码时使用这些素材"},
    {"role": "user",      "content": str('\n').join([
        """请根据以下 PPT 大纲生成 HTML + CSS 形式的 PPT, 内容页使用中国石油 2024 版 PPT 素材:""",
        """1. PPT 封面""",
        """   - 主标题: 2025 年工作计划 (字体大小 48 pt, 居中对齐)""",
        """   - 次级标题: 汇报人 苟风 (字体大小 24 pt, 居中对齐)""",
        """2. PPT 内容 (设计与开发)""",
        """   - 主标题: 设计与开发 (字体大小 24 pt, 左对齐)""",
        """   - 文本内容: 设计与开发工作是 2025 年的主要工作, 将持续进行 UI 创新 (字体大小 16 pt, 左对齐)""",
        """   - 图表内容: 设计工作占比 0.6, 开发工作占比 0.4 (饼图)"""])},
    {"role": "assistant", "content": str('\n').join([
        """```html""",
        """<!DOCTYPE html>""",
        """<html lang="zh-CN">""",
        """<head>""",
        """    <meta charset="UTF-8">""",
        """    <meta name="viewport" content="width=device-width, initial-scale=1.0">""",
        """    <title>2025年工作计划</title>""",
        """    <style>""",
        """        /* 全局样式 */""",
        """        * {""",
        """            margin: 0;""",
        """            padding: 0;""",
        """            box-sizing: border-box;""",
        """        }""",
        """        body {""",
        """            font-family: 'Microsoft YaHei', Arial, sans-serif;""",
        """            background-color: #f5f5f5;""",
        """        }""",
        """ """,
        """        /* PPT容器 */""",
        """        .ppt-container {""",
        """            width: 1280px;""",
        """            height: 720px;""",
        """            margin: 20px auto;""",
        """            position: relative;""",
        """            box-shadow: 0 0 30px rgba(0,0,0,0.2);""",
        """            overflow: hidden;""",
        """        }""",
        """ """,
        """        /* 封面页 */""",
        """        .cover {""",
        """            background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);""",
        """            color: white;""",
        """            height: 100%;""",
        """            display: flex;""",
        """            flex-direction: column;""",
        """            justify-content: center;""",
        """            align-items: center;""",
        """            text-align: center;""",
        """            padding: 0 200px;""",
        """        }""",
        """        .main-title {""",
        """            font-size: 48px;""",
        """            font-weight: bold;""",
        """            margin-bottom: 40px;""",
        """            text-shadow: 0 2px 10px rgba(0,0,0,0.3);""",
        """        }""",
        """        .sub-title {""",
        """            font-size: 24px;""",
        """            padding: 15px 30px;""",
        """            border-top: 2px solid rgba(255,255,255,0.3);""",
        """            border-bottom: 2px solid rgba(255,255,255,0.3);""",
        """        }""",
        """ """,
        """        /* 中国石油风格内容页 */""",
        """        .cnpc-content {""",
        """            height: 100%;""",
        """            background-color: #fff;""",
        """            position: relative;""",
        """            overflow: hidden;""",
        """        }""",
        """        .cnpc-bg {""",
        """            position: absolute;""",
        """            width: 100%;""",
        """            height: 100%;""",
        """            background: url('http://110.41.178.142/resource/ppt.template.cnpc.2024.2.content.1280.720.png') no-repeat;""",
        """            background-size: cover;""",
        """            opacity: 0.9;""",
        """        }""",
        """        .cnpc-content-area {""",
        """            position: relative;""",
        """            padding: 95px 20px 20px;""",
        """            height: 100%;""",
        """            display: grid;""",
        """            grid-template-columns: 1fr 1fr;""",
        """            gap: 40px;""",
        """        }""",
        """        .cnpc-title {""",
        """            font-size: 24px;""",
        """            color: #c00;""",
        """            font-weight: bold;""",
        """            margin-bottom: 30px;""",
        """            grid-column: 1 / -1;""",
        """            padding-left: 20px;""",
        """            border-left: 4px solid #c00;""",
        """        }""",
        """        .cnpc-text {""",
        """            font-size: 16px;""",
        """            line-height: 1.8;""",
        """            color: #333;""",
        """            padding: 20px;""",
        """            background-color: rgba(255,255,255,0.8);""",
        """            border-radius: 4px;""",
        """            box-shadow: 0 2px 10px rgba(0,0,0,0.1);""",
        """        }""",
        """        .cnpc-chart {""",
        """            display: flex;""",
        """            flex-direction: column;""",
        """            align-items: center;""",
        """            justify-content: center;""",
        """        }""",
        """        .cnpc-pie {""",
        """            width: 280px;""",
        """            height: 280px;""",
        """            border-radius: 50%;""",
        """            background: conic-gradient(""",
        """                #c00 0% 60%,""",
        """                #e55 60% 100%""",
        """            );""",
        """            position: relative;""",
        """            box-shadow: 0 5px 15px rgba(0,0,0,0.1);""",
        """            margin-bottom: 20px;""",
        """        }""",
        """        .cnpc-label {""",
        """            position: absolute;""",
        """            font-size: 14px;""",
        """            color: #333;""",
        """            background-color: rgba(255,255,255,0.9);""",
        """            padding: 5px 10px;""",
        """            border-radius: 4px;""",
        """        }""",
        """        .cnpc-label-design {""",
        """            top: 30%;""",
        """            right: -50px;""",
        """        }""",
        """        .cnpc-label-dev {""",
        """            bottom: 30%;""",
        """            left: -50px;""",
        """        }""",
        """        .cnpc-legend {""",
        """            display: flex;""",
        """            gap: 20px;""",
        """        }""",
        """        .cnpc-legend-item {""",
        """            display: flex;""",
        """            align-items: center;""",
        """            font-size: 14px;""",
        """        }""",
        """        .cnpc-legend-color {""",
        """            width: 16px;""",
        """            height: 16px;""",
        """            margin-right: 8px;""",
        """            border-radius: 3px;""",
        """        }""",
        """ """,
        """        /* 导航按钮 */""",
        """        .nav-buttons {""",
        """            display: flex;""",
        """            justify-content: center;""",
        """            gap: 20px;""",
        """            margin-top: 20px;""",
        """        }""",
        """        .nav-button {""",
        """            padding: 10px 20px;""",
        """            background-color: #c00;""",
        """            color: white;""",
        """            border: none;""",
        """            border-radius: 4px;""",
        """            cursor: pointer;""",
        """            transition: all 0.3s;""",
        """        }""",
        """        .nav-button:hover {""",
        """            background-color: #e55;""",
        """        }""",
        """    </style>""",
        """</head>""",
        """<body>""",
        """    <div class="ppt-container" id="ppt-slide">""",
        """        <!-- 封面页 -->""",
        """        <div class="cover" id="slide-1">""",
        """            <h1 class="main-title">2025年工作计划</h1>""",
        """            <p class="sub-title">汇报人 苟风</p>""",
        """        </div>""",
        """ """,
        """        <!-- 中国石油风格内容页 -->""",
        """        <div class="cnpc-content" id="slide-2" style="display:none;">""",
        """            <div class="cnpc-bg"></div>""",
        """            <div class="cnpc-content-area">""",
        """                <h2 class="cnpc-title">设计与开发</h2>""",
        """                <div class="cnpc-text">""",
        """                    <p>设计与开发工作是2025年的主要工作，将持续进行UI创新。我们将重点提升用户体验，优化界面设计流程，加强前端开发能力建设。</p>""",
        """                </div>""",
        """                <div class="cnpc-chart">""",
        """                    <div class="cnpc-pie">""",
        """                        <span class="cnpc-label cnpc-label-design">设计 60%</span>""",
        """                        <span class="cnpc-label cnpc-label-dev">开发 40%</span>""",
        """                    </div>""",
        """                    <div class="cnpc-legend">""",
        """                        <div class="cnpc-legend-item">""",
        """                            <div class="cnpc-legend-color" style="background-color: #c00;"></div>""",
        """                            <span>设计工作</span>""",
        """                        </div>""",
        """                        <div class="cnpc-legend-item">""",
        """                            <div class="cnpc-legend-color" style="background-color: #e55;"></div>""",
        """                            <span>开发工作</span>""",
        """                        </div>""",
        """                    </div>""",
        """                </div>""",
        """            </div>""",
        """        </div>""",
        """    </div>""",
        """ """,
        """    <div class="nav-buttons">""",
        """        <button class="nav-button" onclick="showSlide(1)">封面</button>""",
        """        <button class="nav-button" onclick="showSlide(2)">内容页</button>""",
        """    </div>""",
        """ """,
        """    <script>""",
        """        function showSlide(slideNumber) {""",
        """            // 隐藏所有幻灯片""",
        """            document.querySelectorAll('#ppt-slide > div').forEach(slide => {""",
        """                slide.style.display = 'none';""",
        """            });""",
        """            // 显示选中的幻灯片""",
        """            document.getElementById(`slide-${slideNumber}`).style.display = 'block';""",
        """        }""",
        """    </script>""",
        """</body>""",
        """</html>""",
        """```""",
        """这个 HTML + CSS 形式的 PPT 包含以下特点:""",
        """1. 封面页:""",
        """   - 蓝色渐变背景符合商务风格""",
        """   - 主标题"2025年工作计划"使用48px字体""",
        """   - 副标题"汇报人 苟风"使用24px字体""",
        """   - 文字居中对齐""",
        """2. 内容页:""",
        """   - 使用中国石油2024版PPT模板背景图""",
        """   - 红色(#c00)作为主色调，符合企业VI""",
        """   - 背景采用半透明处理保持内容可读性""",
        """3. 交互功能:""",
        """   - 底部导航按钮可在封面和内容页之间切换""",
        """   - 简单的JavaScript实现幻灯片切换""",
        """4. 响应式设计:""",
        """   - 固定尺寸1280×720像素模拟PPT比例""",
        """   - 使用现代CSS布局(grid和flexbox)"""])}
]

# ##################################################

app = flask.Flask(__name__)

# ##################################################

@app.route("/v1", methods=["GET"])
def api_proxy_openai_v1():

    return '', 200

@app.route("/v1/favicon.ico", methods=["GET"])
def api_proxy_openai_v1_favicon_ico():

    return '', 200

@app.route("/v1/models", methods=["GET"])
def api_proxy_openai_v1_models():

    received_api_key = flask.request.headers.get("Authorization", '')
    # ..............................................
    if (received_api_key == ''):
        return flask.jsonify({"error": "missing api key"}), 401
    # ..............................................
    this_proxy_openai_api = proxy_openai_api_202505.proxy_openai_api()
    this_proxy_openai_api.url.update(f"{SIMILAR_TO_OPENAI_API_BASE_URL}/models")
    this_proxy_openai_api.headers.update({"Authorization": received_api_key}).update({"Content-Type": "application/json"})
    # ..............................................
    rps_dict = this_proxy_openai_api.get()
    # ..............................................
    return flask.jsonify(rps_dict), 200

@app.route("/v1/chat/completions", methods=["POST"])
def api_proxy_openai_v1_chat_completions():

    # Example:
    # - curl -H "Content-Type: application/json" \
    # -      -H "Authorization: Bearer please-enter-openai-api-key" \
    # -      -X POST \
    # -      -d '{"messages": [{"role": "user", "content": "Can you here me?"}]}' \
    # -      http://127.0.0.1:5001/v1/chat/completions
    # Returns:
    # - {'code': 0,
    # -  'message': 'Success',
    # -  'sid': 'cha000bc275@dx196bad449ab9a4b532',
    # -  'choices': [{'message': {'role': 'assistant', 'content': 'Can I help you?'},
    # -               'index': 0}],
    # -  'usage': {'prompt_tokens': 5, 'completion_tokens': 5, 'total_tokens': 10}}

    received_api_key = flask.request.headers.get("Authorization", '')
    # ..............................................
    if (received_api_key == ''):
        return flask.jsonify({"error": "missing api key"}), 401
    # ..............................................
    received_model    = flask.request.json.get("model",    '')
    received_messages = flask.request.json.get("messages", [])
    received_stream   = flask.request.json.get("stream",   False)
    # ..............................................
    # 忽略 JSON 参数中的 "Authorization", "model" (强制调用特定模型)
#   received_api_key  = "Bearer NXuRvfuyBjfsOHqtAyRI:NXuRvfuyBjfsOHqtAyRI"
#   received_model    = "4.0Ultra"
    # ..............................................
    try:
        this_proxy_openai_api = proxy_openai_api_202505.proxy_openai_api()
        this_proxy_openai_api.url.update(f"{SIMILAR_TO_OPENAI_API_BASE_URL}/chat/completions")
        this_proxy_openai_api.headers.update({"Authorization": received_api_key}).update({"Content-Type": "application/json"})
        this_proxy_openai_api.json.update({"model":  received_model})
        this_proxy_openai_api.json.update({"stream": received_stream})
        # ..........................................
        history_messages:list = LLM_MESSAGE_LIST_HTML_CSS_PPT
        # ..........................................
        for message in received_messages:
            history_messages.append(message)
        # ..........................................
        this_proxy_openai_api.json.update({"messages": history_messages})
        # ..........................................
        if (received_stream == True):
            rps_iter = this_proxy_openai_api.post()
            # 流式响应处理
            def generate():
                for chunk in rps_iter:
                    if chunk:
                        yield chunk
            # ......................................
            # OpenAI API 202505 的流式响应通常使用 Server-Sent Events (SSE),
            # 即 text/event-stream 类型,
            # 每个数据块以 "data: " 开头, 后跟 JSON 数据, 并以两个换行符结束
            return flask.Response(generate(), content_type="text/event-stream")
        else:
            rps_dict = this_proxy_openai_api.post()
            # ......................................
            return flask.jsonify(rps_dict), 200
    except Exception as e:
        print("[Debug] error:", str(e))
        return flask.jsonify({"error": str(e)}), 500

if __name__ == '__main__':

#   app.run(host="0.0.0.0", port=5101, debug=True)  # 调试模式
    app.run(host="0.0.0.0", port=5101)

# EOF Signed by GF.
